package phase1.part1;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;

import org.junit.Before;
import org.junit.Test;

/**
 * 
 * 
 * @author John Girata
 * @version Nov 17, 2012
 */
public class RecursiveDescenderTestRegEx {
	private RecursiveDescender rd;

	@Before
	public void setup() {
		rd = new RecursiveDescender(new HashMap<String, CharacterClass>());
	}

	@Test
	public void testRegEx() {
		State s = rd.regEx(makeList("a|(bb+)"));
		// s.print(System.out);
		// System.out.println("ACCEPTING " +
		// RecursiveDescender.findAccepting(s).getLabel());
	}

	@Test
	public void testRexp1() {
		List<Character> text = makeList("abc");

		State start = new State();
		State end = new State(true);
		start.addTransition(end, "d");

		State one = rd.rexp1(text, start);

		assertFalse(one.isAccepting());
		assertEquals(1, one.getTransitions().size());
		assertFalse(one.getTransitions().get(0).isEpsilonTransition());
		assertTrue(one.getTransitions().get(0).accepts('d'));

		List<Character> expected = new LinkedList<>(Arrays.asList('d', 'a',
				'b', 'c'));
		State current = one;

		while (!current.isAccepting()) {
			assertEquals(1, current.getTransitions().size());
			Transition t = current.getTransitions().get(0);

			if (!t.isEpsilonTransition()) {
				assertTrue(t.getPattern().accepts(expected.remove(0)));
			}

			current = t.getEnd();
		}

		assertTrue(expected.isEmpty());
	}

	@Test
	public void testRexp2_0() {
		setup();
		List<Character> text = makeList("a*");

		State start = new State();
		State end = new State(true);
		start.addTransition(end, "b");

		State one = rd.rexp2(text, start);
		assertTrue(text.isEmpty());

		assertFalse(one.isAccepting());
		assertEquals(1, one.getTransitions().size());

		Transition onea = one.getTransitions().get(0);

		assertFalse(onea.isEpsilonTransition());
		assertTrue(onea.accepts('b'));

		State two = onea.getEnd();

		assertFalse(two.isAccepting());
		assertEquals(1, two.getTransitions().size());
		assertTrue(two.getTransitions().get(0).isEpsilonTransition());

		State three = two.getTransitions().get(0).getEnd();

		checkStar(three);
	}

	@Test
	public void testRexp2tail_0() {
		List<Character> text = new ArrayList<>();
		text.add('*');

		State previous = new State();
		previous.addTransition(new State(true), "a");

		State start = rd.rexp2tail(text, previous);
		assertEquals(0, text.size());

		checkStar(start);
	}

	@Test
	public void testRexp2tail_1() {
		List<Character> text = new ArrayList<>();
		text.add('+');

		State previous = new State();
		previous.addTransition(new State(true), "a");

		State start = rd.rexp2tail(text, previous);
		assertEquals(0, text.size());

		assertNotNull(start);
		assertFalse(start.isAccepting());
		assertEquals(1, start.getTransitions().size());

		State oldEnd = start.getTransitions().get(0).getEnd();

		assertFalse(oldEnd.isAccepting());
		assertEquals(1, oldEnd.getTransitions().size());

		Transition t = oldEnd.getTransitions().get(0);
		assertTrue(t.isEpsilonTransition());

		State s = t.getEnd();
		checkStar(s);
	}

	private void checkStar(State start) {
		assertNotNull(start);
		assertFalse(start.isAccepting());
		assertEquals(2, start.getTransitions().size());

		Transition one = start.getTransitions().get(0);
		Transition two = start.getTransitions().get(1);

		assertTrue(one.isEpsilonTransition());
		assertTrue(one.getEnd().isAccepting());
		assertEquals(1, one.getEnd().getTransitions().size());
		assertTrue(one.getEnd().getTransitions().get(0).isEpsilonTransition());
		assertEquals(start, one.getEnd().getTransitions().get(0).getEnd());

		State end = one.getEnd();
		assertTrue(end.isAccepting());

		assertTrue(two.isEpsilonTransition());
		assertTrue(two.getEnd().getTransitions().get(0).getPattern()
				.accepts('a'));
		assertEquals(end, two.getEnd().getTransitions().get(0).getEnd()
				.getTransitions().get(0).getEnd());
	}

	@Test
	public void testRexp3() {
		List<Character> letters = new ArrayList<>();
		letters.add('a');
		letters.add('b');

		State start = rd.rexp3(letters, new State());

		assertEquals(2, letters.size());
		assertNotNull(start);
		assertEquals(1, start.getTransitions().size());
		assertFalse(start.isAccepting());

		Transition t = start.getTransitions().get(0);
		assertTrue(t.isEpsilonTransition());

		State end = t.getEnd();

		assertNotNull(end);
		assertEquals(0, end.getTransitions().size());
		assertTrue(end.isAccepting());
	}

	@Test
	public void testDuplicate0() {
		State a = new State("a");
		State b = new State("b");
		State c = new State("c");

		a.addTransition(b);
		a.addTransition(c);
		b.addTransition(c);
		c.addTransition(b);

		State acopy = RecursiveDescender.duplicate(a);

		assertEquals("a", a.getLabel());
		assertEquals("b", b.getLabel());
		assertEquals("c", c.getLabel());

		assertEquals(2, a.getTransitions().size());
		assertEquals(1, b.getTransitions().size());
		assertEquals(1, c.getTransitions().size());

		assertEquals(b, a.getTransitions().get(0).getEnd());
		assertEquals(c, a.getTransitions().get(1).getEnd());
		assertEquals(c, b.getTransitions().get(0).getEnd());
		assertEquals(b, c.getTransitions().get(0).getEnd());

		assertFalse("a".equals(acopy.getLabel()));
		assertEquals(2, acopy.getTransitions().size());

		State bcopy = acopy.getTransitions().get(0).getEnd();
		State ccopy = acopy.getTransitions().get(1).getEnd();

		assertFalse("b".equals(bcopy.getLabel()));
		assertFalse("c".equals(ccopy.getLabel()));

		assertEquals(1, bcopy.getTransitions().size());
		assertEquals(1, ccopy.getTransitions().size());

		assertEquals(ccopy, bcopy.getTransitions().get(0).getEnd());
		assertEquals(bcopy, ccopy.getTransitions().get(0).getEnd());
	}

	@Test
	public void testDuplicate1() {
		State a = new State("a");
		State b = new State("b");
		State c = new State("c");

		a.addTransition(b);
		b.addTransition(c);
		c.addTransition(a);

		State acopy = RecursiveDescender.duplicate(a);

		assertEquals("a", a.getLabel());
		assertEquals("b", b.getLabel());
		assertEquals("c", c.getLabel());

		assertEquals(1, a.getTransitions().size());
		assertEquals(1, b.getTransitions().size());
		assertEquals(1, c.getTransitions().size());

		assertEquals(b, a.getTransitions().get(0).getEnd());
		assertEquals(c, b.getTransitions().get(0).getEnd());
		assertEquals(a, c.getTransitions().get(0).getEnd());

		assertFalse("a".equals(acopy.getLabel()));
		assertEquals(1, acopy.getTransitions().size());

		State bcopy = acopy.getTransitions().get(0).getEnd();
		assertEquals(1, bcopy.getTransitions().size());

		State ccopy = bcopy.getTransitions().get(0).getEnd();
		assertEquals(1, ccopy.getTransitions().size());

		assertFalse("b".equals(bcopy.getLabel()));
		assertFalse("c".equals(ccopy.getLabel()));

		assertEquals(ccopy, bcopy.getTransitions().get(0).getEnd());
		assertEquals(acopy, ccopy.getTransitions().get(0).getEnd());
	}

	@Test
	public void testEatREChar() {
		testEatREChar("a", 0, "a");
		testEatREChar("\\", 0, "\\\\");
		testEatREChar("a", 2, "abc");
		testEatREChar("\\", 1, "\\\\d");

		assertEquals(0, RecursiveDescender.eatREChar(makeList("\\")).length());
	}

	@Test
	public void testRecursivelySetNotAccepting0() {
		State a = new State("a");
		State b = new State("b");
		State c = new State("c", true);

		a.addTransition(b);
		a.addTransition(c);
		b.addTransition(c);
		c.addTransition(b);

		RecursiveDescender.recursivelySetNotAccepting(a);

		assertFalse(a.isAccepting());
		assertFalse(b.isAccepting());
		assertFalse(c.isAccepting());
	}

	@Test
	public void testRecursivelySetNotAccepting1() {
		State a = new State("a");
		State b = new State("b");
		State c = new State("c", true);

		a.addTransition(b);
		b.addTransition(c);
		c.addTransition(a);

		RecursiveDescender.recursivelySetNotAccepting(a);

		assertFalse(a.isAccepting());
		assertFalse(b.isAccepting());
		assertFalse(c.isAccepting());
	}

	private void testEatREChar(String expected, int size, String input) {
		List<Character> list = makeList(input);

		assertEquals("Content", expected, RecursiveDescender.eatREChar(list));
		assertEquals("Size", size, list.size());
	}

	private List<Character> makeList(String letters) {
		List<Character> list = new LinkedList<>();

		for (char c : letters.toCharArray()) {
			list.add(c);
		}

		return list;
	}
}
